﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using Ams.Extensions;
using TableType = Ams.Mvc.Bootstrap.Table;

namespace Ams.Mvc.Bootstrap
{
    [Flags]
    public enum Table
    {
        Normal = 0,
        Striped = 1,
        Bordered = 2,
        Hover = 4,
        Condensed = 8,
        Responsive = 16
    }

    public static class BootstrapTableExtensions
    {
        #region TableForModel
        public static MvcHtmlString TableForModel(this BootstrapHelper helper)
        {
            return TableForModel(helper, null);
        }
        public static MvcHtmlString TableForModel(this BootstrapHelper helper, Table tableType)
        {
            return TableForModel(helper, tableType, null);
        }
        public static MvcHtmlString TableForModel(this BootstrapHelper helper, object attributes)
        {
            return TableForModel(helper, TableType.Striped | TableType.Bordered, attributes);
        }
        public static MvcHtmlString TableForModel(this BootstrapHelper helper, Table tableType, object attributes)
        {
            var table = TableTagBuilder(helper, tableType, attributes);
            return new MvcHtmlString(table.ToString());
        } 
        #endregion

        #region TableFor
        public static MvcHtmlString TableFor<TModel, TValue>(this BootstrapHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TValue>>> expression)
        {
            return TableFor(helper, expression, TableType.Normal, null);
        }
        public static MvcHtmlString TableFor<TModel, TValue>(this BootstrapHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TValue>>> expression, Table tableType)
        {
            return TableFor(helper, expression, tableType, null);
        }
        public static MvcHtmlString TableFor<TModel, TValue>(this BootstrapHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TValue>>> expression, object attributes)
        {
            return TableFor(helper, expression, TableType.Normal, attributes);
        }
        public static MvcHtmlString TableFor<TModel, TValue>(this BootstrapHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TValue>>> expression, Table tableType, object attributes)
        {
            var items = expression.Compile().Invoke(helper.ViewData.Model);
            return Table(helper, items, tableType, attributes);
        } 
        #endregion

        #region Table
        public static MvcHtmlString Table<T>(this BootstrapHelper helper, IEnumerable<T> itemsEnumerable)
        {
            return Table(helper, itemsEnumerable, TableType.Normal, null);
        }
        public static MvcHtmlString Table<T>(this BootstrapHelper helper, IEnumerable<T> itemsEnumerable, Table tableType)
        {
            return Table(helper, itemsEnumerable, tableType, null);
        }
        public static MvcHtmlString Table<T>(this BootstrapHelper helper, IEnumerable<T> itemsEnumerable, object attributes)
        {
            return Table(helper, itemsEnumerable, TableType.Normal, attributes);
        }
        public static MvcHtmlString Table<T>(this BootstrapHelper helper, IEnumerable<T> itemsEnumerable, Table tableType, object attributes)
        {
            return TableTagBuilder(helper, itemsEnumerable, tableType, attributes).ToMvcHtmlString();
        } 
        #endregion

        #region DataTableForModel
        public static MvcHtmlString DataTableForModel(this BootstrapHelper helper)
        {
            return DataTableForModel(helper, 0);
        }
        public static MvcHtmlString DataTableForModel(this BootstrapHelper helper, int defaultNumOfRowsToDisplay)
        {
            var rowLength = defaultNumOfRowsToDisplay != 0 ? new Dictionary<string, string> { { "data-table-length", defaultNumOfRowsToDisplay.ToString() } } : null;
            var table = TableTagBuilder(helper, TableType.Bordered | TableType.Striped, rowLength);
            table.AddCssClass("cp-data-table");
            return new MvcHtmlString(table.ToString());
        } 
        #endregion

        #region TableTagBuilder UtilityMethods
        private static TagBuilder TableTagBuilder(this BootstrapHelper helper, Table tableType, object attributes)
        {
            var model = helper.ViewData.Model;

            var modelType = model.GetType();
            Type itemType = null;

            //Arrays are handled like this
            if (modelType.IsArray)
            {
                itemType = modelType.GetElementType();
            }
            //Enumerables are handled like this
            else if (modelType.GetInterfaces().Any(t => t.Name == typeof(IEnumerable<>).Name))
            {
                itemType = model.GetType().GetGenericArguments().FirstOrDefault();
            }

            //We should have an item type at this point.
            if (itemType == null)
            {
                throw new NotSupportedException();
            }

            var method = typeof(BootstrapTableExtensions).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).First(i => i.Name == "TableTagBuilder" && i.GetParameters().Count() == 4);
            var genericMethod = method.MakeGenericMethod(new[] { itemType });

            var table = (TagBuilder)genericMethod.Invoke(null, new[] { helper, model, tableType, attributes });
            return table;
        }
        private static TagBuilder TableTagBuilder<T>(this BootstrapHelper helper, IEnumerable<T> itemsEnumerable, Table tableType, object attributes)
        {
            var items = itemsEnumerable.ToList();

            var itemType = items.GetType().GetGenericArguments().First();

            var trbuilder = new TagBuilder("tr")
            {
                InnerHtml = String.Empty
            };

            var propsForTable = new List<PropertyInfo>();
            foreach (var prop in itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (prop.Attribute<HiddenInputAttribute>() != null) continue;
                trbuilder.InnerHtml += prop.DisplayName().WrapInTag("td");
                propsForTable.Add(prop);
            }

            var itemTrList = new List<string>();
            foreach (var item in items)
            {
                var itemTrbuilder = new TagBuilder("tr")
                {
                    InnerHtml = String.Empty
                };

                foreach (var prop in propsForTable)
                {
                    itemTrbuilder.InnerHtml += (prop.GetValue(item, null) ?? String.Empty).ToString().WrapInTag("td");
                }
                itemTrList.Add(itemTrbuilder.ToString());
            }

            var table = new TagBuilder("table")
            {
                InnerHtml = trbuilder.ToString().WrapInTag("thead") + itemTrList.Join(Environment.NewLine).WrapInTag("tbody")
            };
            if (attributes != null) table.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(attributes), true);
            table.AddCssClass("table");

            if (tableType.HasFlag(TableType.Striped))
                table.AddCssClass("table-striped");

            if (tableType.HasFlag(TableType.Bordered))
                table.AddCssClass("table-bordered");

            if (tableType.HasFlag(TableType.Hover))
                table.AddCssClass("table-hover");

            if (tableType.HasFlag(TableType.Condensed))
                table.AddCssClass("table-condensed");

            if (tableType.HasFlag(TableType.Responsive))
            {
                var wrapper = new TagBuilder("div");
                wrapper.AddCssClass("table-responsive");
                wrapper.InnerHtml = table.ToString();
                return wrapper;
            }

            return table;
        } 
        #endregion
    }
}
